//
// Created by Administrator on 2021/1/31 0031.
//

#include "JqhAudio.h"

JqhAudio::JqhAudio(JqhPlayStatus *playStatus, int sample_rate, CallJava *callJava) {
    this->playStatus = playStatus;
    queue = new JqhQueue(playStatus);
    this->sample_rate = sample_rate;
    buffer = static_cast<uint8_t  *>(av_malloc(sample_rate * 2 * 2));
    this->callJava = callJava;

    //  soundtouch
    sampleBuffer = static_cast<SAMPLETYPE  *>(malloc(sample_rate * 2 * 2));
    soundTouch = new SoundTouch();

    soundTouch->setSampleRate(sample_rate);
    soundTouch->setChannels(2);

    soundTouch->setPitch(pitch);
    soundTouch->setTempo(speed);
}

JqhAudio::~JqhAudio() {

}

void *decodPlay(void *data) {
    JqhAudio *jqhAudio = (JqhAudio *) (data);
    jqhAudio->initOpenSLES();
    pthread_exit(&jqhAudio->thread_play);
}
// 播放
void JqhAudio::play() {
    pthread_create(&thread_play, NULL, decodPlay, this);
}

// 重采样
int JqhAudio::resumpleAudio(void **pcmbuf) {
    data_size = 0;
    while(playStatus != NULL && !playStatus->exit) {

        if (playStatus->seek){
           // av_usleep(1000 * 100);
            continue;
        } ;
        if (queue->getQueueSize() == 0) {
            if (!playStatus->load) {
                playStatus->load = true;
                callJava->onCallLoad(CHILD_THREAD, true);
            }
            //av_usleep(1000 * 100);
            continue;
        } else {
            if (playStatus->load) {
                playStatus->load = false;
                callJava->onCallLoad(CHILD_THREAD, false);
            }
        }

        if (readFrameFinished) {
            avPacket = av_packet_alloc();
            if (queue->getAVPacket(avPacket) != 0) {
                LOGD("getAVPacket error");
                // 取出失败
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
            // 把packet发送给解码器解码avframe
            ret = avcodec_send_packet(avCodecContext, avPacket);
//        if (LOG_DEBUG) {
//            LOGD("avcodec_send_packet ret = %d" , ret);
//        }
            if (ret != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
        }

        avFrame = av_frame_alloc();
        // 接受解码后的数据
        ret = avcodec_receive_frame(avCodecContext, avFrame);
//        if (LOG_DEBUG) {
//            LOGD("avcodec_receive_frame ret = %d" , ret);
//        }
        if (ret == 0) {
            readFrameFinished = false;
            if (avFrame->channels > 0 && avFrame->channel_layout == 0) {
                // 根据声道数获取声道布局
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);
            } else if(avFrame->channels == 0 && avFrame->channel_layout > 0)  {
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
            }

            SwrContext *swr_ctx = NULL;
            swr_ctx = swr_alloc_set_opts(
                    NULL,
                    AV_CH_LAYOUT_STEREO,
                    AV_SAMPLE_FMT_S16,
                    avFrame->sample_rate,
                    avFrame->channel_layout,
                    static_cast<AVSampleFormat >(avFrame->format),
                            avFrame->sample_rate,
                            NULL, NULL
                    );
            if (!swr_ctx || swr_init(swr_ctx) < 0) {
                if (LOG_DEBUG) {
                    LOGD("swr_init error");
                }
                // 失败
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                if (swr_ctx != NULL ) {
                    swr_free(&swr_ctx);
                    swr_ctx = NULL;
                }
                readFrameFinished = true;
                continue;
            }

            // 开始重采样
            nb = swr_convert(
                    swr_ctx,
                    &buffer,
                    avFrame->nb_samples,
                    (const uint8_t **)( avFrame->data),
                    avFrame->nb_samples
                    );

            int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
            data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
//            if (LOG_DEBUG) {
//                LOGD("解码数据 data_size=%d", data_size);
//            }
            now_time = avFrame->pts * av_q2d(time_base);
            if (now_time < clock) {
                now_time = clock;
            }
            clock = now_time;
            *pcmbuf = buffer;
//            av_packet_free(&avPacket);
//            av_free(avPacket);
//            avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            swr_free(&swr_ctx);
            swr_ctx = NULL;
            break;
        } else {
            readFrameFinished = true;
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            continue;
        }
    }
    return data_size;
}

void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void *context) {

    JqhAudio *audio = (JqhAudio *)(context);
    if (audio != NULL) {
        int buffersize = audio->getSoundTouchData();
        if (buffersize > 0) {
            audio->clock += buffersize / ((double)(audio->sample_rate * 2 * 2));
            if (audio->clock - audio->last_time >= 0.1) { // 0.1s回调一次
                audio->last_time = audio->clock;
                audio->callJava->onCallTimeInfo(CHILD_THREAD, audio->clock, audio->duration);
            }
            if (audio->isRecordPcm) {
                audio->callJava->onCallPcmToAAC(CHILD_THREAD, buffersize * 2 * 2, audio->sampleBuffer);
            }
            // 计算分贝值
            audio->callJava->onCallValumeDB(CHILD_THREAD,
                    audio->getPCMDB(reinterpret_cast<char *>(audio->sampleBuffer), buffersize * 4));
            (*audio->pcmBufferQueue)->Enqueue(audio->pcmBufferQueue,audio->sampleBuffer, buffersize * 2 * 2);
        }
    }
}

void JqhAudio::initOpenSLES() {

    LOGD("initOpenSLES...")
    SLresult result;
    //第一步------------------------------------------
    // 创建引擎对象
    slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);


    //第二步-------------------------------------------
    // 创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void)result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void)result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void)result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};

    // 创建播放器
    LOGD("创建播放器")
    SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,
            2,
            getCurrentSampleRateForOpensles(sample_rate),
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
            SL_BYTEORDER_LITTLEENDIAN
    };

    SLDataSource slDataSource = {&android_queue, &pcm};

    SLDataSink audioSnk = {&outputMix, NULL};
    const SLInterfaceID ids[5] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME, SL_IID_MUTESOLO, SL_IID_PLAYBACKRATE};
    const SLboolean req[5] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 5, ids, req);

    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
    // 获取声道接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmMutePlay);
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);

    setVolume(volumePercent);
    setMute(mute);
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);

    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);

    pcmBufferCallBack(pcmBufferQueue, this);

}

int JqhAudio::getCurrentSampleRateForOpensles(int sample_rate) {

    int rate = 0;
    switch (sample_rate)
    {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate =  SL_SAMPLINGRATE_44_1;
    }
    return rate;
}

void JqhAudio::pause() {
    if (pcmPlayerObject != NULL) {
        LOGD("call pause")
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
    }
}

void JqhAudio::resume() {
    if (pcmPlayerObject != NULL) {
        LOGD("call resume")
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    }
}

void JqhAudio::stop() {
    if (pcmPlayerObject != NULL) {
        LOGD("call stop")
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }
}

void JqhAudio::release() {
    stop();
    if (queue != NULL) {
        delete(queue);
        queue = NULL;
    }

    if (pcmPlayerObject != NULL) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        pcmPlayerPlay = NULL;
        pcmBufferQueue = NULL;
        pcmMutePlay = NULL;
        pcmPlayerVolume = NULL;
    }

    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }

    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;
    }

    if (buffer != NULL) {
        free(buffer);
        buffer = NULL;
    }

    if (avCodecContext != NULL) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
    }

    if (out_buffer != NULL) {
        out_buffer = NULL;
    }

    if (soundTouch != NULL) {
        delete soundTouch;
        soundTouch = NULL;
    }

    if (sampleBuffer != NULL) {
        free(sampleBuffer);
        sampleBuffer = NULL;
    }

    playStatus = NULL;
    callJava = NULL;
}

void JqhAudio::setVolume(int percent) {
    volumePercent = percent;
    if (pcmPlayerVolume != NULL) {
        LOGE("setVolume %d",percent)
        if(percent > 30)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -20);
        }
        else if(percent > 25)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -22);
        }
        else if(percent > 20)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -25);
        }
        else if(percent > 15)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -28);
        }
        else if(percent > 10)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -30);
        }
        else if(percent > 5)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -34);
        }
        else if(percent > 3)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -37);
        }
        else if(percent > 0)
        {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -40);
        }
        else{
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -100);
        }
    }
}

void JqhAudio::setMute(int mute) {
    this->mute = mute;
    if (pcmMutePlay != NULL) {
        if (mute == 0 ) {  // left
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, true);
        } else if (mute == 1) { // right
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, true);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);
        } else if (mute == 2) { // center
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);
        }
    }
}

int JqhAudio::getSoundTouchData() {
    while(playStatus != NULL && !playStatus->exit){
        out_buffer = NULL;
        if (finished) {
            finished = false;
            data_size = resumpleAudio(reinterpret_cast<void **>(&out_buffer));
            if (data_size > 0) {
                for (int i = 0 ; i < data_size / 2 + 1 ; i++) {
                    sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
                }
                soundTouch->putSamples(sampleBuffer, nb);
                num = soundTouch->receiveSamples(sampleBuffer, data_size /4);
            }else {
                soundTouch->flush();
            }
        }

        if (num == 0) {
            finished = true;
            continue;
        } else {
            if(out_buffer == NULL) {
                num = soundTouch->receiveSamples(sampleBuffer, data_size / 4);
                if (num == 0) {
                    finished = true;
                    continue;
                }
            }
            return num;
        }
    }
    return 0;
}

void JqhAudio::setPitch(float pitch) {
    this->pitch = pitch;
    if (soundTouch != NULL) {
        soundTouch->setPitch(pitch);
    }


}

void JqhAudio::setSpeed(float speed) {
    this->speed = speed;
    if (soundTouch != NULL) {
        soundTouch->setTempo(speed);
    }
}

int JqhAudio::getPCMDB(char *pcmdata, size_t pcmsize) {
    int db = 0 ;
    short int pervalue = 0 ;
    double sum = 0 ;
    for (int i = 0 ; i < pcmsize; i+=2) {
        memcpy(&pervalue, pcmdata + i, 2);
        sum += abs(pervalue);
    }
    sum = sum / (pcmsize / 2);
    if (sum > 0) {
        db = 20.0 * log10(sum);
    }
    return db;
}

void JqhAudio::startStopRecord(bool start) {
    this->isRecordPcm = start;

}
